#ifndef __spi_lcd
#define __spi_lcd

#include "main.h"
#include "usart.h"

#include "lcd_fonts.h" // 图片和字库文件不是必须，用户可自行删减
#include "lcd_image.h"

#include <stdarg.h>

/*----------------------------------------------- 参数宏 -------------------------------------------*/

#define LCD_Width  240 // LCD的像素长度
#define LCD_Height 240 // LCD的像素宽度

// 显示方向参数
// 使用示例：LCD_DisplayDirection(Direction_H) 设置屏幕横屏显示
// #define Direction_H      0 // LCD横屏显示
// #define Direction_H_Flip 1 // LCD横屏显示,上下翻转
// #define Direction_V      2 // LCD竖屏显示
// #define Direction_V_Flip 3 // LCD竖屏显示,上下翻转
typedef enum {
    Direction_H = 0x00U,
    Direction_H_Flip,
    Direction_V,
    Direction_V_Flip
} DisplayDirection_TypeDef;

// 设置变量显示时多余位补0还是补空格
// 只有 LCD_DisplayNumber() 显示整数 和 LCD_DisplayDecimals()显示小数 这两个函数用到
// 使用示例： LCD_ShowNumMode(Fill_Zero) 设置多余位填充0，例如 123 可以显示为 000123
#define Fill_Zero  0 // 填充0
#define Fill_Space 1 // 填充空格

/*---------------------------------------- 常用颜色 ------------------------------------------------------

 1. 这里为了方便用户使用，定义的是24位 RGB888颜色，然后再通过代码自动转换成 16位 RGB565 的颜色
 2. 24位的颜色中，从高位到低位分别对应 R、G、B  3个颜色通道
 3. 用户可以在电脑用调色板获取24位RGB颜色，再将颜色输入LCD_SetColor()或LCD_SetBackColor()就可以显示出相应的颜色
 */

typedef struct {
    uint8_t B; // 蓝色通道
    uint8_t G; // 绿色通道
    uint8_t R; // 红色通道
} RGB888_ColorTypeDef;
typedef RGB888_ColorTypeDef RGB_ColorTypeDef;

typedef union {
    struct {
        uint16_t B : 5; // 蓝色通道，占 5 位
        uint16_t G : 6; // 绿色通道，占 6 位
        uint16_t R : 5; // 红色通道，占 5 位
    };
    uint16_t value; // 16 位整型表示
} RGB565_ColorTypeDef;

#define RGB(color)                   \
    (RGB888_ColorTypeDef){           \
        .R = ((color) >> 16) & 0xFF, \
        .G = ((color) >> 8) & 0xFF,  \
        .B = ((color)) & 0xFF}

#define RGB565(color)     \
    (RGB565_ColorTypeDef) \
    {                     \
        .value = (color)  \
    }

#define RGB_888To565(color)  \
    (RGB565_ColorTypeDef){   \
        .R = (color).R >> 3, \
        .G = (color).G >> 2, \
        .B = (color).B >> 3}

#define LCD_WHITE         RGB(0xFFFFFFF & 0x00FFFFFF) //	纯白色
#define LCD_BLACK         RGB(0x0000000 & 0x00FFFFFF) //	纯黑色

#define LCD_BLUE          RGB(0x0000FF & 0x00FFFFFF) //	纯蓝色
#define LCD_GREEN         RGB(0x00FF00 & 0x00FFFFFF) //	纯绿色
#define LCD_RED           RGB(0xFF0000 & 0x00FFFFFF) //	纯红色
#define LCD_CYAN          RGB(0x00FFFF & 0x00FFFFFF) //	蓝绿色
#define LCD_MAGENTA       RGB(0xFF00FF & 0x00FFFFFF) //	紫红色
#define LCD_YELLOW        RGB(0xFFFF00 & 0x00FFFFFF) //	黄色
#define LCD_GREY          RGB(0x2C2C2C & 0x00FFFFFF) //	灰色

#define LCD_LIGHT_BLUE    RGB(0x8080FF & 0x00FFFFFF) //	亮蓝色
#define LCD_LIGHT_GREEN   RGB(0x80FF80 & 0x00FFFFFF) //	亮绿色
#define LCD_LIGHT_RED     RGB(0xFF8080 & 0x00FFFFFF) //	亮红色
#define LCD_LIGHT_CYAN    RGB(0x80FFFF & 0x00FFFFFF) //	亮蓝绿色
#define LCD_LIGHT_MAGENTA RGB(0xFF80FF & 0x00FFFFFF) //	亮紫红色
#define LCD_LIGHT_YELLOW  RGB(0xFFFF80 & 0x00FFFFFF) //	亮黄色
#define LCD_LIGHT_GREY    RGB(0xA3A3A3 & 0x00FFFFFF) //	亮灰色

#define LCD_DARK_BLUE     RGB(0x000080 & 0x00FFFFFF) //	暗蓝色
#define LCD_DARK_GREEN    RGB(0x008000 & 0x00FFFFFF) //	暗绿色
#define LCD_DARK_RED      RGB(0x800000 & 0x00FFFFFF) //	暗红色
#define LCD_DARK_CYAN     RGB(0x008080 & 0x00FFFFFF) //	暗蓝绿色
#define LCD_DARK_MAGENTA  RGB(0x800080 & 0x00FFFFFF) //	暗紫红色
#define LCD_DARK_YELLOW   RGB(0x808000 & 0x00FFFFFF) //	暗黄色
#define LCD_DARK_GREY     RGB(0x404040 & 0x00FFFFFF) //	暗灰色

typedef struct {
    RGB565_ColorTypeDef Color;          //	LCD当前画笔颜色
    RGB565_ColorTypeDef BackColor;      //	背景色
    uint8_t ShowNum_Mode;               // 数字显示模式
    DisplayDirection_TypeDef Direction; //	显示方向
    uint16_t Width;                     // 屏幕像素长度
    uint16_t Height;                    // 屏幕像素宽度
    uint8_t X_Offset;                   // X坐标偏移，用于设置屏幕控制器的显存写入方式
    uint8_t Y_Offset;                   // Y坐标偏移，用于设置屏幕控制器的显存写入方式
} LCD_ConfigTypeDef;

extern LCD_ConfigTypeDef LCD;

/*------------------------------------------------ 函数声明 ----------------------------------------------*/

void SPI_LCD_Init(void);                                                     // 液晶屏以及SPI初始化
void LCD_Clear(void);                                                        // 清屏函数
void LCD_ClearRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height); // 局部清屏函数

void LCD_SetAddress(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); // 设置坐标
void LCD_SetColor(RGB_ColorTypeDef Color);                               //	设置画笔颜色
void LCD_SetBackColor(RGB_ColorTypeDef Color);                           //	设置背景颜色
void LCD_SetDirection(DisplayDirection_TypeDef direction);               //	设置显示方向

//>>>>>	显示ASCII字符
void LCD_SetAsciiFont(pFONT *fonts);                           //	设置ASCII字体
void LCD_DisplayChar(uint16_t x, uint16_t y, uint8_t c);       //	显示单个ASCII字符
void LCD_DisplayString(uint16_t x, uint16_t y, const char *p); //	显示ASCII字符串

//>>>>>	显示中文字符，包括ASCII码
void LCD_SetTextFont(pFONT *fonts);                                 // 设置文本字体，包括中文和ASCII字体
void LCD_DisplayChinese(uint16_t x, uint16_t y, const char *pText); // 显示单个汉字
void LCD_DisplayText(uint16_t x, uint16_t y, const char *pText);    // 显示字符串，包括中文和ASCII字符
// 格式化显示字符串

[[gnu::format(printf, 3, 4)]] void LCD_Printf(uint16_t x, uint16_t y, const char *fmt, ...);

//>>>>>	显示整数或小数
void LCD_ShowNumMode(uint8_t mode);                                                         // 设置变量显示模式，多余位填充空格还是填充0
void LCD_DisplayNumber(uint16_t x, uint16_t y, int32_t number, uint8_t len);                // 显示整数
void LCD_DisplayDecimals(uint16_t x, uint16_t y, double number, uint8_t len, uint8_t decs); // 显示小数

//>>>>>	2D图形函数
void LCD_DrawPoint(uint16_t x, uint16_t y); // 画点

void LCD_DrawLine_V(uint16_t x, uint16_t y, uint16_t height);          // 画垂直线
void LCD_DrawLine_H(uint16_t x, uint16_t y, uint16_t width);           // 画水平线
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); // 两点之间画线

void LCD_DrawRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height); // 画矩形
void LCD_DrawCircle(uint16_t x, uint16_t y, uint16_t r);                    // 画圆
void LCD_DrawEllipse(int x, int y, int r1, int r2);                         // 画椭圆

//>>>>>	区域填充函数
void LCD_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height); // 填充矩形
void LCD_FillCircle(uint16_t x, uint16_t y, uint16_t r);                    // 填充圆

//>>>>>	绘制单色图片
void LCD_DrawImage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *pImage);

// 绘制彩色图片
void LCD_DrawColoredImage(uint16_t x, uint16_t y, const ColorImage_TypeDef *pImage);

/*------------------------------------------------------  引脚配置宏 -------------------------------------------------*/

/**
 * @brief  LCD SPI接口时钟初始化，使用硬件 SPI3
 * @note GPIOB3 -> SPI3_SCK
 * @note GPIOB5 -> SPI3_MOSI
 * @note D11 -> LCD_CS, GPIO_Output, 推挽输出
 * @note D12 -> LCD_DC, GPIO_Output, 推挽输出
 * @note D13 -> LCD_Backlight, GPIO_Output, 推挽输出
 */

#define LCD_SCK_PIN  SPI3_SCK_Pin       // SCK引脚， 需要重定义SPI3的IO口复用
#define LCD_SCK_PORT SPI3_SCK_GPIO_Port // SCK引脚用到的端口
// #define GPIO_LCD_SCK_CLK __HAL_RCC_GPIOB_CLK_ENABLE() // SCK引脚IO口时钟

#define LCD_SDA_PIN  SPI3_MOSI_Pin       // SDA引脚， 需要重定义SPI3的IO口复用
#define LCD_SDA_PORT SPI3_MOSI_GPIO_Port // SDA引脚用到的端口
// #define GPIO_LCD_SDA_CLK __HAL_RCC_GPIOB_CLK_ENABLE() // SDA引脚IO口时钟

#define LCD_CS_PIN  LCD_CS_Pin       // CS片选引脚，低电平有效
#define LCD_CS_PORT LCD_CS_GPIO_Port // CS引脚用到的端口
// #define GPIO_LCD_CS_CLK        __HAL_RCC_GPIOD_CLK_ENABLE() // CS引脚IO口时钟

#define LCD_DC_PIN  LCD_DC_Pin       // 数据指令选择  引脚
#define LCD_DC_PORT LCD_DC_GPIO_Port // 数据指令选择  GPIO端口
// #define GPIO_LCD_DC_CLK        __HAL_RCC_GPIOD_CLK_ENABLE() // 数据指令选择  GPIO时钟

#define LCD_Backlight_PIN  LCD_Backlight_Pin       // 背光  引脚
#define LCD_Backlight_PORT LCD_Backlight_GPIO_Port // 背光 GPIO端口
// #define GPIO_LCD_Backlight_CLK __HAL_RCC_GPIOD_CLK_ENABLE() // 背光 GPIO时钟

/*--------------------------------------------------------- 控制宏 ---------------------------------------------------*/

// // 因为片选引脚需要频繁操作，使用寄存器效率会高些
// #define LCD_CS_H          LCD_CS_PORT->BSRR = LCD_CS_PIN                  // 片选拉高
// #define LCD_CS_L          LCD_CS_PORT->BSRR = (uint32_t)LCD_CS_PIN << 16U // 片选拉低

#define LCD_CS_H          HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET);   // 片选拉高
#define LCD_CS_L          HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET); // 片选拉低

#define LCD_DC_Command    HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_RESET) // 低电平，指令传输
#define LCD_DC_Data       HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET)   // 高电平，数据传输

#define LCD_Backlight_ON  HAL_GPIO_WritePin(LCD_Backlight_PORT, LCD_Backlight_PIN, GPIO_PIN_SET)   // 高电平，开启背光
#define LCD_Backlight_OFF HAL_GPIO_WritePin(LCD_Backlight_PORT, LCD_Backlight_PIN, GPIO_PIN_RESET) // 低电平，关闭背光

#endif //__spi_lcd
